﻿//#define PRINT_MATS
using System;
using System.Linq;
using System.Collections.Generic;
using RayDen.Library.Components.SystemComponents;
using RayDen.Library.Entity.Scene;
using RayDen.Library.Components.Surface;

namespace RayDen.Library.Components
{
    public class BsdfLibrary : MaterialLibraryBase<SurfaceMaterial>
    {

        public BsdfLibrary()
            : base(SurfaceMaterials.MatteMaterial)
        {
        }


        public override void Populate(MaterialInfo[] materials)
        {
            this.surfMats.Clear();

            foreach (var materialInfo in materials)
            {
                /*
                if (materialInfo.Exponent <= 0f)
                    surfMats.Add(materialInfo.Name.ToLower(), new MatteMaterial(materialInfo.Kd) { MaterialData = materialInfo });
                else
                {
                    surfMats.Add(materialInfo.Name.ToLower(), new DistributionBsdf(-materialInfo.Exponent * 0.001f, materialInfo.Kd));
                    //surfMats.Add(materialInfo.Name.ToLower(), new DistributionBsdf(materialInfo.Exponent ,materialInfo.Kd));
                }
                //surfMats.Add(materialInfo.Name.ToLower(), new MatteMaterial(materialInfo.Kd) { MaterialData = materialInfo });
                continue;
                */

                var mat = SurfaceMaterials.CreateMaterial(materialInfo);
                this.surfMats.Add(materialInfo.Name.ToLower(), mat);
#if PRINT_MATS
                Tracer.TraceLine("###Material - {0} -  {1}", materialInfo.Name, mat.First.GetType().Name);
#endif

            }
        }
    }


    public class OptimizedBsdfLibrary : IMaterialLibrary<ISurfaceMaterial>
    {
        private SurfaceMaterial[] surfMats;
        private Dictionary<string, int> names;

        public OptimizedBsdfLibrary()
        {
            surfMats = new SurfaceMaterial[100];
            names = new Dictionary<string, int>();
        }

        public SurfaceMaterial[] Materials
        {
            get { return surfMats; }
        }

        public int GetID(string name)
        {
            return names[name];
        }


        public void Populate(MaterialInfo[] materials)
        {
            var materialsCount = materials.Count(item => item != null);
            if (materialsCount >= surfMats.Length)
            {
                Array.Resize(ref surfMats, materialsCount);
            }
            for (int index = 0; index < materialsCount; index++)
            {
                var materialInfo = materials[index];
                /*
                if (materialInfo.Exponent <= 0f)
                    surfMats.Add(materialInfo.Name.ToLower(), new MatteMaterial(materialInfo.Kd) { MaterialData = materialInfo });
                else
                {
                    surfMats.Add(materialInfo.Name.ToLower(), new DistributionBsdf(-materialInfo.Exponent * 0.001f, materialInfo.Kd));
                    //surfMats.Add(materialInfo.Name.ToLower(), new DistributionBsdf(materialInfo.Exponent ,materialInfo.Kd));
                }
                //surfMats.Add(materialInfo.Name.ToLower(), new MatteMaterial(materialInfo.Kd) { MaterialData = materialInfo });
                continue;
                */


                names.Add(materialInfo.Name.ToLower(), index);
                var mat = SurfaceMaterials.CreateMaterial(materialInfo);

                this.surfMats[index] = mat;
                Tracer.TraceLine("###Material - {0} -  {1} - ID {2} ", materialInfo.Name, mat.First.GetType().Name, index);
            }
        }

        public void Add(ISurfaceMaterial[] surfMatsToAdd)
        {
            var result = new List<SurfaceMaterial>(this.surfMats.Length + surfMatsToAdd.Length);
            result.AddRange(this.surfMats);
            int index = this.surfMats.Length;
            foreach (var surfMat in surfMatsToAdd)
            {
                Tracer.TraceLine("###Material Added- {0} -  {1} - ID {2} ", surfMat.Name, surfMat.Type, index);

                result.Add((SurfaceMaterial)surfMat);
                names.Add(surfMat.Name.ToLower(), index);
                index++;
            }
            this.surfMats = result.ToArray();
        }

        public void Merge(ISurfaceMaterial[] mats)
        {
            foreach (var surfaceMaterial in mats)
            {
                for (int index = 0; index < surfMats.Count(m => m != null); index++)
                {
                    if (surfMats[index].Name.Equals(surfaceMaterial.Name, StringComparison.InvariantCultureIgnoreCase))
                    {
                        surfMats[index] = (SurfaceMaterial)surfaceMaterial;
                    }
                }
            }
        }

        #region IMaterialLibraryBase<SurfaceMaterial> Members

        public ISurfaceMaterial GetSurfMat(string name)
        {
            return this.surfMats[names[name]];
        }

        public ISurfaceMaterial GetSurfMat(int id)
        {
            return this.surfMats[id];
        }

        public void Replace(string name, SurfaceMaterial bsdf)
        {
            if (bsdf == null)
            {
                throw new ArgumentNullException("bsdf");
            }

            if (!names.ContainsKey(name.ToLower()))
            {
                return;
                throw new ArgumentException("name");
            }

            surfMats[names[name.ToLower()]] = bsdf;
        }
        #endregion

        ISurfaceMaterial IMaterialLibraryBase<ISurfaceMaterial>.GetSurfMat(string name)
        {
            return this.GetSurfMat(name);
        }

        void IMaterialLibraryBase<ISurfaceMaterial>.Populate(MaterialInfo[] mats)
        {
            this.Populate(mats);
        }

        public IEnumerator<ISurfaceMaterial> GetEnumerator()
        {
            return this.surfMats.ToList().GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.surfMats.GetEnumerator();
        }
    }
}